local lsp_conf = {}

function lsp_conf.preview_location(location, context, before_context)
	-- location may be LocationLink or Location (more useful for the former)
	context = context or 15
	before_context = before_context or 0
	local uri = location.targetUri or location.uri
	if uri == nil then
		return
	end
	local bufnr = vim.uri_to_bufnr(uri)
	if not vim.api.nvim_buf_is_loaded(bufnr) then
		vim.fn.bufload(bufnr)
	end

	local range = location.targetRange or location.range
	local contents = vim.api.nvim_buf_get_lines(
		bufnr,
		range.start.line - before_context,
		range["end"].line + 1 + context,
		false
	)
	local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
	return vim.lsp.util.open_floating_preview(contents, filetype, { border = "single" })
end

function lsp_conf.preview_location_callback(_, result)
	local context = 15
	if result == nil or vim.tbl_isempty(result) then
		return nil
	end
	if vim.tbl_islist(result) then
		lsp_conf.floating_buf, lsp_conf.floating_win = lsp_conf.preview_location(result[1], context)
	else
		lsp_conf.floating_buf, lsp_conf.floating_win = lsp_conf.preview_location(result, context)
	end
end

function lsp_conf.PeekDefinition()
	if vim.tbl_contains(vim.api.nvim_list_wins(), lsp_conf.floating_win) then
		vim.api.nvim_set_current_win(lsp_conf.floating_win)
	else
		local params = vim.lsp.util.make_position_params()
		return vim.lsp.buf_request(0, "textDocument/definition", params, lsp_conf.preview_location_callback)
	end
end

function lsp_conf.PeekTypeDefinition()
	if vim.tbl_contains(vim.api.nvim_list_wins(), lsp_conf.floating_win) then
		vim.api.nvim_set_current_win(lsp_conf.floating_win)
	else
		local params = vim.lsp.util.make_position_params()
		return vim.lsp.buf_request(0, "textDocument/typeDefinition", params, lsp_conf.preview_location_callback)
	end
end

function lsp_conf.PeekImplementation()
	if vim.tbl_contains(vim.api.nvim_list_wins(), lsp_conf.floating_win) then
		vim.api.nvim_set_current_win(lsp_conf.floating_win)
	else
		local params = vim.lsp.util.make_position_params()
		return vim.lsp.buf_request(0, "textDocument/implementation", params, lsp_conf.preview_location_callback)
	end
end
function lsp_conf.goto_definition(split_cmd)
	local util = vim.lsp.util
	local log = require("vim.lsp.log")
	local api = vim.api

	-- note, this handler style is for neovim 0.5.1/0.6, if on 0.5, call with function(_, method, result)
	local handler = function(_, result, ctx)
		if result == nil or vim.tbl_isempty(result) then
			local _ = log.info() and log.info(ctx.method, "No location found")
			return nil
		end

		if split_cmd then
			vim.cmd(split_cmd)
		end

		if vim.tbl_islist(result) then
			util.jump_to_location(result[1])

			if #result > 1 then
				util.setqflist(util.locations_to_items(result))
				api.nvim_command("copen")
				api.nvim_command("wincmd p")
			end
		else
			util.jump_to_location(result)
		end
	end

	return handler
end

return lsp_conf
